<?php
/**
 * Copyright (c) [2019] [吴跃忠]
 * [selibra] is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 * http://license.coscl.org.cn/MulanPSL
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v1 for more details.
 */

namespace Selibra\Command;

use Selibra\Command\Annotations\Command;
use Selibra\Command\Events\CommandServerConfigureEvent;
use Selibra\Command\Events\CommandServerStartEvent;
use Selibra\Command\Exceptions\NotFoundCommandException;
use Selibra\Event\EventTrigger;
use Selibra\Tools\Console;
use Selibra\Di\Annotations\Autowired;
use Selibra\Di\Annotations\ServerStartedComponent;
use Selibra\Di\ContainerObjectInitialization;
use Selibra\Di\DI;
use Swoole\Process;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class Server
 * @package Selibra\Command
 */
#[Command("server")]
class Server extends \Symfony\Component\Console\Command\Command
{

    /**
     * @var ServerProcessManager
     */
    #[Autowired]
    protected ServerProcessManager $processManager;

    /**
     * @var OutputInterface
     */
    public static OutputInterface $output;

    /**
     * @var InputInterface
     */
    public static InputInterface $input;

    /**
     * @var Server
     */
    private static Server $server;

    public static function getInstance()
    {
        return self::$server ?? null;
    }


    /**
     * Server constructor.
     * @param string|null $name
     */
    public function __construct(string $name = null)
    {
        parent::__construct($name);
    }

    protected function configure()
    {
        $this->setName('server');
        $this->addArgument('servers', InputArgument::REQUIRED);
        $this->addOption('--ignore-dir', '', InputOption::VALUE_OPTIONAL);
        $this->addOption('--run-dir', '', InputOption::VALUE_OPTIONAL);
        $this->addOption('--name', '', InputOption::VALUE_OPTIONAL);
        $this->addOption('--config-dir', '', InputOption::VALUE_OPTIONAL);
    }


    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     * @throws NotFoundCommandException
     * @throws \ReflectionException
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        Server::$server = $this;
        Server::$input = $input;
        Server::$output = $output;
        $servers = explode(',', $input->getArgument('servers'));
        $commands = [];

        ContainerObjectInitialization::componentMethodRun(ServerStartedComponent::class);

        foreach ($servers as $server) {
            $serverData = explode('-', $server);
            $command = array_shift($serverData);
            $command = CommandCollector::getCommandClass($command);
            if (empty($command)) {
                throw new NotFoundCommandException();
            }
            $commands[] = ['command' => $command, 'serverData' => $serverData];
        }

        $serversCount = count($servers);
        $workers = [];
        foreach ($commands as $command) {
            $process = $this->newProcess($command, $output);
            $process->useQueue();
            $pid = $process->start();
            $workers[$pid] = $process;
            $this->processManager->addServer($pid, $workers, $command['command']);
            // 监听器
            EventTrigger::listener((new CommandServerStartEvent($command)));
        }
        for ($n = $serversCount; $n > 0; $n--) {
            $status = Process::wait(true);
            echo "Recycled #{$status['pid']}, code={$status['code']}, signal={$status['signal']}" . PHP_EOL;
        }
        return 0;
    }


    /**
     * @param $command
     * @param $output
     * @return Process
     */
    public function newProcess($command, $output)
    {
        $process = new Process(function (Process $proc) use ($command, $output) {
            $serverData = $command['serverData'];
            try {
                $inputData = [];
                foreach ($serverData as $datum) {
                    $inputItem = explode(':', $datum);
                    $inputData[$inputItem[0]] = $inputItem[1];
                }
                $input = new ArrayInput($inputData);
                /** @var \Symfony\Component\Console\Command\Command $serverCommand */
                $serverCommand = DI::getObjectContext($command['command']);
                // 通讯代理
                if (!empty($serverCommand)) {
                    $serverCommand->execute($input, $output);
                }
            } catch (\Exception $exception) {
                Console::log($exception->getMessage());
            }
            while (1) {

            }
        });
        return $process;
    }

}
